using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using T.FAS.Runtime.Base.Entity.Tools;
using T.ServiceComponent.Common.Service;
using T.ServiceComponent.I18N.Service;

namespace T.FAS.Runtime.Base.Entity
{
    public static class TResultBuilder
    {
        /// <summary>
        /// 根据TSchema将数据集合返回TResult结构的数据
        /// </summary>
        /// <param name = "ds">数据</param>
        /// <param name = "schema">TSchema结构</param>
        /// <returns></returns>
        public static TResult BuildTResult(object datacontent, TSchema schema)
        {
            if (datacontent == null)
                throw new ArgumentNullException("TResultBuilder.BuildTResult Error,datacontent is null");
            if (schema == null || schema.Entity == null)
                throw new ArgumentNullException("TResultBuilder.BuildTResult Error,shcema is null");
            var result = new TResult();
            if (schema.MetadataType == TEntityType.SGEO)
            {
                result = BuildExtTResult<JObject>(datacontent, schema);
            }
            else
            {
                var dataTables = new List<DataTable>();
                if (datacontent is DataSet)
                {
                    foreach (DataTable dt in (datacontent as DataSet).Tables)
                    {
                        dataTables.Add(dt);
                    }

                    result = BuildFASTResultFromListTables(dataTables, schema);
                }
                else if (datacontent is List<DataTable>)
                {
                    dataTables = (datacontent as List<DataTable>);
                    result = BuildFASTResultFromListTables(dataTables, schema);
                }
                else if (datacontent is DataTable)
                {
                    dataTables = new List<DataTable>()
                    {datacontent as DataTable};
                    result = BuildFASTResultFromListTables(dataTables, schema);
                }
                else if (datacontent is TCommonResult<TRecord>)
                {
                    result = BuildFASTResultFromTRecord(datacontent as TCommonResult<TRecord>, schema);
                }
                else
                {
                    throw new ArgumentOutOfRangeException("Unknow Type Convert To TResult");
                }
            }

            return result;
        }

        /// <summary>
        /// 根据TSchema将数据集合返回FAS平台外数据TResult结构的数据
        /// </summary>
        /// <param name = "dataContent">数据</param>
        /// <param name = "schema">TSchema结构</param>
        /// <returns></returns>
        private static TResult BuildExtTResult<T>(object dataContent, TSchema schema)
        {
            var result = new TCommonResult<T>();
            result.ResultType = schema.ResultType;
            result.EntityType = schema.MetadataType;
            result.DataContent = dataContent;
            return result;
        }

        /// <summary>
        /// 根据TSchema将数据集合返回FAS平台内基础数据TResult结构的数据
        /// </summary>
        /// <param name = "ds">数据</param>
        /// <param name = "schema">TSchema结构</param>
        /// <returns></returns>
        private static TResult BuildFASTResult(DataSet ds, TSchema schema)
        {
            var tables = new List<DataTable>();
            foreach (DataTable table in ds.Tables)
            {
                tables.Add(table);
            }

            return BuildFASTResultFromListTables(tables, schema);
        }

        private static TResult BuildFASTResultFromListTables(List<DataTable> tables, TSchema schema)
        {
            if (tables == null || tables.Count <= 0)
                throw new FasException(BaseExceptionCode.TRecordBuildError, "TResultBuilder.BuildTResult Error,tables is null");
            if (schema == null || schema.Entity == null)
                throw new FasException(BaseExceptionCode.TRecordBuildError, "TResultBuilder.BuildTResult Error,shcema is null");
            var result = new TCommonResult<TRecord>();
            result.ResultType = schema.ResultType;
            result.Record = new TRecord();
            result.List = new TArray<TRecord>(schema.Entity);
            result.EntityType = schema.MetadataType;
            result.Schema = schema;
            var dataResultType = schema.ResultType;
            if (schema.MetadataType == TEntityType.DQ)
            {
                BuildFASDQResultFromTable(tables, schema.Entity, result, dataResultType);
            }
            else
            {
                BuildFASCDOResultFromListTables(tables, schema, result, dataResultType);
            }

            //result.Schema = null;
            return result;
        }

        private static TResult BuildFASTResultFromTRecord(TCommonResult<TRecord> data, TSchema schema)
        {
            return data;
        }

        private static void BuildFASCDOResultFromListTables(List<DataTable> tables, TSchema schema, TCommonResult<TRecord> result, DataType dataResultType)
        {
            var table = tables.Find(t => t.TableName.Equals(schema.Entity.MetadataCode));
            if (table == null)
                return;
            var pkColumnName = "";
            var pkColumnAttribute = schema.Entity.Attributes.Find(x => x.IsPrimaryKey);
            if (pkColumnAttribute != null)
                pkColumnName = pkColumnAttribute.Code;
            if (dataResultType == DataType.Record)
            {
                if (table.Rows.Count > 0)
                {
                    var propertiesDic = new Dictionary<string, object>();
                    BuildRecordProperties(table.Rows[0], tables, schema.Entity, propertiesDic);
                    result.Record = new TRecord(propertiesDic, pkColumnName);
                    result.Count = 1;
                    result.List = null;
                }
            }
            else
            {
                foreach (DataRow row in table.Rows)
                {
                    var propertiesDic = new Dictionary<string, object>();
                    BuildRecordProperties(row, tables, schema.Entity, propertiesDic);
                    result.List.Add(new TRecord(propertiesDic, pkColumnName));
                }

                result.List.Rows = table.Rows.Count;
                var totalDataTable = tables.Find(t => t.TableName.Equals(schema.Entity.MetadataCode + "_TotalCount"));
                if (totalDataTable != null && totalDataTable.Rows != null && totalDataTable.Rows.Count.Equals(1))
                {
                    result.Count = Convert.ToInt16(totalDataTable.Rows[0][0]);
                }
            }
        }

        private static void BuildFASDQResultFromTable(List<DataTable> tables, TEntity entity, TCommonResult<TRecord> result, DataType dataResultType, string parentValue = "")
        {
            if (entity.Attributes == null || entity.Attributes.Count <= 0)
                throw new Exception("DQ Attributes is nulll");
            var table = tables.Find(x => x.TableName.Equals(entity.MetadataCode, StringComparison.OrdinalIgnoreCase));
            if (table != null)
            {
                foreach (DataRow row in table.Rows)
                {
                    //需要按照父级进行子集数据过滤
                    if (!string.IsNullOrEmpty(parentValue))
                    {
                        var targetId = entity.Relation.ToTAttributeCode;
                        var targetValue = Convert.ToString(row[targetId]);
                        if (!targetValue.Equals(parentValue))
                            continue;
                    }

                    var propertiesDic = new Dictionary<string, object>();
                    foreach (TAttribute attr in entity.Attributes)
                    {
                        if (attr.DataType == DataType.Record)
                        {
                            if (attr.Entity != null && attr.Entity.Attributes != null && attr.Entity.Attributes.Count > 0)
                            {
                                foreach (TAttribute p in attr.Entity.Attributes)
                                {
                                    if (!p.IsVirtual)
                                    {
                                        if (p.DataType != DataType.List && p.DataType != DataType.Record)
                                        {
                                            if (TryGetSpecialData(entity, p, row, out object data))
                                            {
                                                propertiesDic[p.Code] = data;
                                            }
                                            else if (table.Columns.Contains(p.Code))
                                            {
                                                propertiesDic[p.Code] = row[p.Code];
                                            }
                                            else
                                            {
                                                propertiesDic[p.Code] = null;
                                            }
                                        }
                                        else
                                        {
                                            throw new Exception($"DQ {entity.MetadataCode} DO {attr.Entity.MetadataCode} Attribute {p.Code} type can not be list or record");
                                        }
                                    }
                                }
                            }
                        }
                        else if (attr.DataType != DataType.List)
                        {
                            if (TryGetSpecialData(entity, attr, row, out object data))
                                propertiesDic[attr.Code] = data;
                            else
                                propertiesDic[attr.Code] = row[attr.Code];
                        }
                        else
                        {
                            if (attr.EntityType == TEntityType.DQ)
                            {
                                //子查询需要按照数据进行过滤
                                if (attr.Entity.Relation == null)
                                    throw new FasException(BaseExceptionCode.TRecordBuildError, $"{TermService.Current.GetText("T_FAS_fasr_Base_1532845295", $"主子DQ中的Relation不能为空")}");
                                var sourceId = attr.Entity.Relation.FromTAttributeCode;
                                var parentFilterValue = Convert.ToString(row[sourceId]);
                                var subResult = new TCommonResult<TRecord>();
                                subResult.ResultType = attr.Entity.ResultType;
                                subResult.Record = new TRecord();
                                subResult.Schema = new TSchema();
                                subResult.List = new TArray<TRecord>(attr.Entity);
                                subResult.EntityType = attr.Entity.MetadataType;
                                BuildFASDQResultFromTable(tables, attr.Entity, subResult, attr.Entity.ResultType, parentFilterValue);
                                propertiesDic[attr.Entity.MetadataCode] = subResult;
                            }
                            else
                            {
                                throw new Exception($"DQ {entity.MetadataCode} Attribute {attr.Code} can not be list");
                            }
                        }
                    }

                    result.List.Add(new TRecord(propertiesDic));
                }

                result.List.Rows = table.Rows.Count;
            }
        }

        /// <summary>
        /// 构造Record的属性
        /// </summary>
        /// <param name = "row">数据行</param>
        /// <param name = "tables">数据集合</param>
        /// <param name = "entity">实体结构</param>
        /// <param name = "propertiesDic">属性集合</param>
        private static void BuildRecordProperties(DataRow row, List<DataTable> tables, TEntity entity, Dictionary<string, object> propertiesDic)
        {
            foreach (var p in entity.Attributes)
            {
                if (!p.IsVirtual)
                {
                    if (p.DataType == DataType.List)
                    {
                        var table = tables.Find(t => t.TableName.Equals(p.Entity.MetadataCode));
                        if (table != null)
                        {
                            var fromDataID = Convert.ToString(row[p.Entity.Relation.FromTAttributeCode]);
                            var listDataTable = table;
                            //var propName = TUtil.GetPropertyName(p.Code, true);
                            var propName = p.Code;
                            propertiesDic[propName] = BuildRecords(fromDataID, listDataTable, tables, p.Entity);
                        }
                    }
                    else if (p.DataType == DataType.Record)
                    {
                        var table = tables.Find(t => t.TableName.Equals(p.Entity.MetadataCode));
                        if (table != null)
                        {
                            var subTable = table;
                            DataRow[] subRows = subTable.Select($"{p.Entity.Relation.ToTAttributeCode}='{Convert.ToString(row[p.Entity.Relation.FromTAttributeCode])}'");
                            if (subRows.Length > 0)
                            {
                                var subRow = subRows[0];
                                propertiesDic[p.Code] = BuildRecord(subRow, tables, p.Entity);
                            }
                            else
                            {
                                //subRows.Length == 0 是否需要异常抛出逻辑。待晶哥确认
                            }
                        }
                    }
                    else
                    {
                        SetPropertyValue(entity, propertiesDic, p, row);
                    }
                }
            }
        }

        private static void SetPropertyValue(TEntity entity, Dictionary<string, object> propertiesDic, TAttribute p, DataRow row)
        {
            object value = row.Table.Columns.Contains(p.Code) ? row[p.Code] : null;
            try
            {
                if (p.ExtProperties != null)
                {
                    if (p.ExtProperties.ContainsKey("IsTimestamp") && bool.TryParse(Convert.ToString(p.ExtProperties["IsTimestamp"]), out bool isTimestamp) && isTimestamp)
                    {
                        if (value is DBNull)
                        {
                            value = null;
                        }

                        var ts = new Timestamp { Value = Convert.ToDateTime(value) };
                        ts.Data = ts.ToString();
                        propertiesDic[p.Code] = ts;
                    }
                    else if (TryGetSpecialData(entity, p, row, out object data))
                    {
                        propertiesDic[p.Code] = data;
                    }
                    else
                    {
                        propertiesDic[p.Code] = value;
                    }
                }
                else
                    propertiesDic[p.Code] = value;
            }
            catch (Exception e)
            {
                throw new Exception($"{TermService.Current.GetTextFormatted("T_FAS_fasr_Base_1585884639", "属性:{0}设置值失败，value:{1}\r\nerr:{2}\r\ne:{3}", p.Code, value, e.Message, e)}", e);
            }
        }

        /// <summary>
        /// 构造Record数据
        /// </summary>
        /// <param name = "tables">数据集合</param>
        /// <param name = "entity">entity结构</param>
        /// <returns></returns>
        private static TRecord BuildRecord(DataRow row, List<DataTable> tables, TEntity entity)
        {
            TRecord record = new TRecord();
            var propertiesDic = new Dictionary<string, object>();
            var pkColumnName = "";
            var pkAttribute = entity.Attributes.Find(x => x.IsPrimaryKey);
            if (pkAttribute != null)
                pkColumnName = pkAttribute.Code;
            foreach (var p in entity.Attributes)
            {
                if (!p.IsVirtual)
                {
                    if (p.DataType == DataType.List)
                    {
                        var table = tables.Find(t => t.TableName.Equals(p.Entity.MetadataCode));
                        var fromDataID = Convert.ToString(row[p.Entity.Relation.FromTAttributeCode]);
                        var listDataTable = table;
                        var listRecord = BuildRecords(fromDataID, listDataTable, tables, p.Entity);
                        //propertiesDic.Add(TUtil.GetPropertyName(p.Code,true), listRecord);
                        propertiesDic.Add(p.Code, listRecord);
                    }
                    else if (p.DataType == DataType.Record)
                    {
                        var table = tables.Find(t => t.TableName.Equals(p.Entity.MetadataCode));
                        var subTable = table;
                        DataRow[] subRows = subTable.Select($"{p.Entity.Relation.ToTAttributeCode}='{Convert.ToString(row[p.Entity.Relation.FromTAttributeCode])}'");
                        if (subRows.Length > 0)
                        {
                            propertiesDic.Add(p.Code, BuildRecord(subRows[0], tables, p.Entity));
                        }
                        //propertiesDic.Add(TUtil.GetPropertyName(p.Code,false), BuildRecord(subRow, tables, p.Entity));
                    }
                    else
                    {
                        SetPropertyValue(entity, propertiesDic, p, row);
                    }
                }
            }

            record = new TRecord(propertiesDic, pkColumnName);
            return record;
        }

        /// <summary>
        /// 构造ListRecord
        /// </summary>
        /// <param name = "dt">数据表</param>
        /// <param name = "ds">数据集合</param>
        /// <param name = "entity">实体结构</param>
        /// <returns></returns>
        private static List<TRecord> BuildRecords(string parentID, DataTable dt, List<DataTable> tables, TEntity entity)
        {
            List<TRecord> list = new List<TRecord>();
            var subRows = dt.Select($"{entity.Relation.ToTAttributeCode}='{parentID}'");
            if (subRows == null || subRows.Length <= 0)
            {
                //当只有主表数据，但是暂时还没有子表数据的时候，返回空集合
                //throw new Exception("SubRow is Null");
                return list;
            }

            foreach (DataRow row in subRows)
            {
                TRecord data = BuildRecord(row, tables, entity);
                list.Add(data);
            }

            return list;
        }

        /// <summary>
        /// 尝试获取特殊属性的值，例如掩码字段等
        /// </summary>
        /// <param name="attr"></param>
        /// <param name="row"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        private static bool TryGetSpecialData(TEntity entity, TAttribute attr, DataRow row, out object data)
        {
            data = null;
            if (attr.ExtProperties != null)
            {
                bool maskColumnFlag = attr.ExtProperties.TryGetValue("MaskRule", out object ruleVal) && ruleVal is MaskingRule;
                bool isMaskColumnFlag = attr.ExtProperties.TryGetValue("IsMaskingColumn", out object isVal) && isVal is bool isMaskColumn && isMaskColumn;
                bool isEnableMaskingFlag = attr.ExtProperties.TryGetValue("IsEnableMasking", out object isEnableMaskingVal) && isEnableMaskingVal is bool isEnableMasking && isEnableMasking;
                if (maskColumnFlag && isEnableMaskingFlag)
                {
                    MaskingRule rule = ruleVal as MaskingRule;
                    //启用掩码的列
                    if (!isMaskColumnFlag)
                    {
                        //todo: 根据配置项-是否启动低代码掩码存储处理
                        string originStrVal = row[attr.Code]?.ToString();
                        //带权限获取掩码
                        try
                        {
                            data = Maskservice.Current.DesensitzeValueWithAuth(rule.RuleID, originStrVal);
                        }
                        catch
                        {
                            //DQ无身份认证调试时使用
                            data = row[attr.Code];
                        }
                        return true;
                    }
                    //掩码列
                    if (isMaskColumnFlag && attr.ExtProperties.TryGetValue("OriginMaskingColumn", out object oriColNameObj) && oriColNameObj is string oriColName && !string.IsNullOrEmpty(oriColName))
                    {
                        string oriVal = row[oriColName]?.ToString() ?? string.Empty;
                        //固定获取掩码，不走权限
                        if (rule.MaskingType == MaskingType.System)
                        {
                            //全部掩码时，返回字符串长度的*号
                            if (rule.IsPartialMasking == false)
                            {
                                data = new string('*', oriVal.Length);
                            }
                            else
                            {
                                try
                                {
                                    data = Maskservice.Current.DesensitzeValue(rule.RuleID, oriVal);
                                }
                                catch
                                {
                                    //抛异常后，掩码失败，返回字符串长度的*号
                                    data = new string('*', oriVal.Length);
                                }
                            }
                        }
                        else
                        {
                            data = MaskRegTool.GetMaskDataInternal(rule.RegPattern, rule.Replacement, oriVal);
                            ////该方法目前走权限，后续自己实现
                            //data = Maskservice.Current.DesensitzeValue(oriVal?.ToString(), new DesensitizationConfig
                            //{
                            //    Pattern = rule.RegPattern,
                            //    Replacement = rule.Replacement,
                            //});
                        }
                        return true;
                    }
                }
            }
            return false;
        }
    }
}